﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Routing;
using System.Web;
using System.Collections.Specialized;
using System.Web.Security;
using System.Web.Mvc;
using System.Net.Http;
using System.ServiceModel.Channels;

using Microsoft.Practices.Unity;

using ERPStore.Extensions;

using CloudSoft.Extensions;

namespace ERPStore.Web.Extensions
{
	public static class HttpExtensions
	{
		/// <summary>
		/// Return the first the or default cached item.
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="cache">The cache.</param>
		/// <param name="predicate">The predicate.</param>
		/// <returns></returns>
		public static T FirstOrDefault<T>(this System.Web.Caching.Cache cache, Func<T, bool> predicate)
		{
			var list = cache.Cast<System.Collections.DictionaryEntry>()
				.Where(i => i.Value.GetType() == typeof(T))
				.Select(i => i.Value).Cast<T>();

			return list.FirstOrDefault(predicate);
		}

		/// <summary>
		/// Gets the list of.
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="cache">The cache.</param>
		/// <param name="predicate">The predicate.</param>
		/// <returns></returns>
		public static IEnumerable<T> GetListOf<T>(this System.Web.Caching.Cache cache, Func<T, bool> predicate)
		{
			var list = cache.Cast<System.Collections.DictionaryEntry>()
						.Where(i => i.Value.GetType() == typeof(T))
						.Select(i => i.Value).Cast<T>();

			return list.Where(predicate);
		}

		/// <summary>
		/// Gets the list of.
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="cache">The cache.</param>
		/// <returns></returns>
		public static IQueryable<T> GetListOf<T>(this System.Web.Caching.Cache cache)
		{
			var list = cache.Cast<System.Collections.DictionaryEntry>()
						.Where(i => i.Value.GetType() == typeof(T))
						.Select(i => i.Value).Cast<T>();

			return list.AsQueryable();
		}

		/// <summary>
		/// Récupération ou création de l'id unique du visiteur
		/// </summary>
		/// <param name="ctx">The HttpContext.</param>
		/// <param name="isNewVisitor">if set to <c>true</c> [is new visitor].</param>
		/// <returns>Id unique du visiteur</returns>
		/// <remarks>
		/// Le cookie dure 60 jours par defaut
		/// </remarks>
		public static string GetOrCreateVisitorId(this System.Web.HttpContextBase ctx, out bool isNewVisitor)
		{
			var visitorCookie = ctx.Request.Cookies["erpstorevid"];
			string visitorId = Guid.NewGuid().ToString().Replace("-", "").Substring(0, 30);
			isNewVisitor = (visitorCookie == null);
			bool needResponse = false;
			var tld = ctx.GetDomainAndTLD();
			SetupVisitorCookie(ref visitorCookie, out visitorId, out needResponse, tld);
			if (needResponse)
			{
				try
				{
					ctx.Response.Cookies.Add(visitorCookie);
				}
				catch
				{
				}
			}
			return visitorId;
		}

		public static string GetDomainAndTLD(this System.Web.HttpContextBase ctx)
		{
			var tokens = ctx.Request.Url.Host.Split('.');
			return GetDomainAndTLD(tokens);
		}

		public static string GetDomainAndTLD(this System.Web.HttpContext ctx)
		{
			var tokens = ctx.Request.Url.Host.Split('.');
			return GetDomainAndTLD(tokens);
		}

		public static string GetDomainAndTLD(this string[] tokens)
		{
			if (tokens.Length < 2)
			{
				return null;
			}
			// TODO : Checker l'ip directe
			var result = string.Format("{0}.{1}", tokens[tokens.Length - 2], tokens[tokens.Length - 1]);
			return result;
		}

		/// <summary>
		/// Récupération ou création de l'id unique du visiteur
		/// </summary>
		/// <param name="ctx">The HttpContext.</param>
		/// <param name="isNewVisitor">if set to <c>true</c> [is new visitor].</param>
		/// <returns>Id unique du visiteur</returns>
		/// <remarks>
		/// Le cookie dure 60 jours par defaut
		/// </remarks>
		public static string GetOrCreateVisitorId(this System.Web.HttpContext ctx, out bool isNewVisitor)
		{
			var visitorCookie = ctx.Request.Cookies["erpstorevid"];
			isNewVisitor = (visitorCookie == null);
			string visitorId = Guid.NewGuid().ToString().Replace("-", "").Substring(0, 30); 
			bool needResponse = false;
			SetupVisitorCookie(ref visitorCookie, out visitorId, out needResponse, ctx.GetDomainAndTLD());
			if (needResponse)
			{
				try
				{
					ctx.Response.Cookies.Add(visitorCookie);
				}
				catch
				{
					
				}
			}
			return visitorId;
		}

		public static string GetControllerActionName(this string url)
		{
			if (url.IsNullOrTrimmedEmpty())
			{
				return "home/index";
			}
			var fakeContext = new ERPStore.Web.MockHttpContext(url);
			return fakeContext.GetControllerActionName();
		}

		public static string GetControllerActionName(this HttpContextBase context)
		{
			RouteData currentRoute = null;
			try
			{
				currentRoute = RouteTable.Routes.GetRouteData(context); // as System.Web.Routing.RouteData;
			}
			catch
			{
				/* Tests */
			}
			if (currentRoute == null)
			{
				return null;
			}
			string controller = string.Format("{0}", currentRoute.Values["controller"] ?? "home");
			string action = string.Format("{0}", currentRoute.Values["action"] ?? "index");
			var controllerActionName = string.Format("{0}/{1}", controller, action);
			return controllerActionName.ToLower();
		}

		private static void SetupVisitorCookie(ref System.Web.HttpCookie cookie, out string vid, out bool needResponse, string domainAndTld)
		{
			needResponse = false;
			if (cookie == null)
			{
				cookie = new System.Web.HttpCookie("erpstorevid");
				cookie.Expires = DateTime.Now.AddDays(60);
				cookie.Path = "/";
				var cookieDomain = GlobalConfiguration.Configuration.Settings.CookieDomain;
				if (!cookieDomain.IsNullOrTrimmedEmpty())
				{
					cookie.Domain = cookieDomain;
				}

				vid = Guid.NewGuid().ToString().Replace("-", "").Substring(0, 30);
				var value = string.Format("{0}|{1:yyyy|MM|dd}", vid, DateTime.Now);

				cookie.Value = value;
				needResponse = true;
			}
			// Si le cookie expire dans moins de 5 jours, alors on ajoute 60 jours
			else
			{
				var tokens = cookie.Value.Split('|');
				DateTime expiration = DateTime.Today;
				if (tokens.Length == 4)
				{
					vid = tokens[0];
					try
					{
						expiration = new DateTime(Convert.ToInt32(tokens[1]), Convert.ToInt32(tokens[2]), Convert.ToInt32(tokens[3]));
					}
					catch
					{
						expiration = DateTime.Today;
					}
				}
				else
				{
					if (!tokens[0].IsNullOrTrimmedEmpty())
					{
						vid = tokens[0];
					}
					else
					{
						vid = Guid.NewGuid().ToString().Replace("-", "").Substring(0, 30);
					}
				}
				if (expiration.AddDays(-5) <= DateTime.Today)
				{
					cookie.Expires = DateTime.Today.AddDays(60);
					cookie.Value = string.Format("{0}|{1:yyyy|MM|dd}", vid, cookie.Expires);
					needResponse = true;
				}
			}
		}

		public static string GetSearchKeywords(this HttpContext context)
		{
			if (context.Request["qr"] != null)
			{
				return context.Request["qr"];
			}
			return context.Request.UrlReferrer.GetSearchKeywords();
		}

		[Obsolete("use leadsourceservice instead", false)]
		public static string GetSearchKeywords(this HttpContextBase context)
		{
			if (context.Request["qr"] != null)
			{
				return context.Request["qr"];
			}
			if (context.Request["qre"] != null)
			{
				var decoded = string.Empty;
				try
				{
					var buffer = System.Convert.FromBase64String(context.Request["qre"]);
					decoded = System.Text.Encoding.UTF8.GetString(buffer);
				}
				catch
				{
				}
				return decoded;
			}
			return context.Request.UrlReferrer.GetSearchKeywords();
		}


		/// <summary>
		/// Indique si l'utilisateur en cours est associé à une société
		/// </summary>
		/// <param name="principal">The principal.</param>
		/// <returns>
		/// 	<c>true</c> if the specified principal has corporate; otherwise, <c>false</c>.
		/// </returns>
		public static bool HasCorporate(this System.Security.Principal.IPrincipal principal)
		{
			var user = ((ERPStore.Models.UserPrincipal) principal).CurrentUser;
			return user != null && user.Corporate != null;
		}


		/// <summary>
		/// Ajout d'un cookie securisé contenant l'id du user
		/// </summary>
		/// <param name="response">The response.</param>
		/// <param name="userId">The user id.</param>
		/// <param name="rememberMe">The remember me.</param>
		public static void AddAuthenticatedCookie(this System.Web.HttpResponseBase response, int userId, bool? rememberMe)
		{
			// Creation d'un ticket encrypté avec le machineKey du serveur en cours
			// La période de validité du cookie est de 30 jours par defaut
			var cookie = CreateAuthenticatedCookie(userId, rememberMe);

			response.Cookies.Remove(FormsAuthentication.FormsCookieName); 
			response.Cookies.Add(cookie);
		}

		public static HttpCookie CreateAuthenticatedCookie(int userId, bool? rememberMe)
		{
			var expirationDays = GlobalConfiguration.Configuration.Settings.CookieExpirationDelayInDays.GetValueOrDefault(30);
			var ticket = new FormsAuthenticationTicket(
				2,
				userId.ToString(),
				DateTime.Now,
				DateTime.Now.AddDays(expirationDays),
				rememberMe.GetValueOrDefault(false),
				"",
				FormsAuthentication.FormsCookiePath);

			var ticketEncrypted = FormsAuthentication.Encrypt(ticket);

			var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, ticketEncrypted);
			cookie.HttpOnly = true;
			cookie.Path = FormsAuthentication.FormsCookiePath;
			cookie.Secure = FormsAuthentication.RequireSSL;
			// TODO : Utiliser le paramètre de domaine (a tester)
			var cookieDomain = GlobalConfiguration.Configuration.Settings.CookieDomain
								?? FormsAuthentication.CookieDomain;
			if (cookieDomain != null)
			{
				cookie.Domain = cookieDomain;
			}
			cookie.Expires = ticket.Expiration;

			return cookie;
		}

		public static System.Net.Http.Headers.CookieHeaderValue ToCookieHeaderValue(this HttpCookie cookie)
		{
			var result = new System.Net.Http.Headers.CookieHeaderValue(cookie.Name, cookie.Value);
			result.Expires = cookie.Expires;
			result.Domain = cookie.Domain;
			result.Path = cookie.Path;
			result.Secure = cookie.Secure;
			result.HttpOnly = cookie.HttpOnly;
			return result;
		}

		public static void SignOut(this System.Web.HttpContextBase context)
		{
			var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, "empty");
			cookie.HttpOnly = true;
			cookie.Path = FormsAuthentication.FormsCookiePath;
			cookie.Secure = FormsAuthentication.RequireSSL;
			var cookieDomain = GlobalConfiguration.Configuration.Settings.CookieDomain
								?? FormsAuthentication.CookieDomain;
			if (cookieDomain != null)
			{
				cookie.Domain = cookieDomain;
			}
			cookie.Expires = DateTime.Now.AddDays(-5);
			context.Response.Cookies.Remove(FormsAuthentication.FormsCookieName);
			context.Response.Cookies.Add(cookie);
		}

		public static System.Web.Mvc.UrlHelper GetUrlHelper(this System.Web.HttpContext ctx)
		{
			if (ctx == null)
			{
				return null;
			}
			var httpContextBase = new System.Web.HttpContextWrapper(ctx);
			var routeData = new RouteData();
			var requestContext = new System.Web.Routing.RequestContext(httpContextBase, routeData);
			var urlHelper = new UrlHelper(requestContext);
			return urlHelper;
		}

		public static HttpRequestBase GetRequestBase(this HttpRequestMessage request)
		{
			if (request == null
				|| request.Properties == null)
			{
				return null;
			}

			if (request.Properties.ContainsKey("MS_HttpContext"))
			{
				return ((HttpContextWrapper)request.Properties["MS_HttpContext"]).Request;
			}
			return null;
		}

		public static string GetClientIpAddress(this HttpRequestMessage request)
		{
			var requestBase = request.GetRequestBase();
			if (requestBase != null)
			{
				return requestBase.UserHostAddress;
			}
			else if (request == null)
			{
				return null;
			}
			else if (request.Properties.ContainsKey(RemoteEndpointMessageProperty.Name))
			{
				RemoteEndpointMessageProperty prop;
				prop = (RemoteEndpointMessageProperty)request.Properties[RemoteEndpointMessageProperty.Name];
				return prop.Address;
			}
			else
			{
				return null;
			}
		}

		public static string GetVisitorId(this HttpRequestMessage request)
		{
			var cookies = request.Headers.GetCookies("erpstorevid");
			if (cookies == null)
			{
				return null;
			}
			var cookie = cookies.FirstOrDefault();
			if (cookie == null)
			{
				return null;
			}
			var visitorId = cookie.Cookies.First().Value.Split('|')[0];
			return visitorId;
		}

		public static void AddWebContext(this Exception ex, System.Web.HttpContextBase ctx)
		{
			if (ctx.Request.Url.Host.IndexOf("localhost") == -1)
			{
				var isNewVisitor = false;
				var visitorId = ctx.ApplicationInstance.Context.GetOrCreateVisitorId(out isNewVisitor);
				ex.Data.Add("machineName", Environment.MachineName);
				ex.Data.Add("host", ctx.Request.Url.Host);
				ex.Data.Add("visitorId", visitorId);
				ex.Data.Add("userHostAddress", ctx.Request.UserHostAddress);
				ex.Data.Add("userHostName", ctx.Request.UserHostName);
				// ex.Data.Add("language", Locale.CurrentCultureName);
				ex.Data.Add("url", ctx.Request.RawUrl);
				ex.Data.Add("referer", ctx.Request.UrlReferrer);
				ex.Data.Add("applicationPath", ctx.Request.ApplicationPath);
				ex.Data.Add("user-agent", ctx.Request.Headers["User-Agent"]);
				ex.Data.Add("cookie", ctx.Request.Headers["Cookie"]);
				if (ctx.Request.Form.Count > 0)
				{
					ex.Data.Add("begin-form", "-----------------------");
					foreach (var item in ctx.Request.Form.AllKeys)
					{
						ex.Data.Add(item, ctx.Request.Form[item]);
					}
					ex.Data.Add("end-form", "-----------------------");
				}
				if (ctx.User.Identity.IsAuthenticated)
				{
					ex.Data.Add("user", ((ERPStore.Models.UserPrincipal) ctx.User).CurrentUser.Login);
				}
				else
				{
					ex.Data.Add("user", "anonymous");
				}
			}
		}

		public static System.Collections.Specialized.NameValueCollection GetFullParameters(this HttpContextBase context)
		{
			return context.Request.Url.GetParameters();
		}

		public static IEnumerable<ERPStore.Models.WebParameter> GetWebContextParameterList(this HttpContextBase context)
		{
			var result = new List<ERPStore.Models.WebParameter>();

			var uriList = context.Request.Url.GetParameters();
			foreach (var key in uriList.AllKeys)
			{
				if (key == null)
				{
					continue;
				}
				result.Add(new ERPStore.Models.WebParameter()
				{
					Type = ERPStore.Models.WebParameterType.Uri,
					Name = key,
					Value = uriList[key]
				});
			}

			foreach (var key in context.Request.Form.AllKeys)
			{
				if (key == null)
				{
					continue;
				}
				result.Add(new ERPStore.Models.WebParameter()
				{
					Type = ERPStore.Models.WebParameterType.Form,
					Name = key,
					Value = uriList[key]
				});
			}

			var parts = context.Request.Url.Host.Split('.').Reverse().ToList();
			if (parts.Count() > 0)
			{
				result.Add(new ERPStore.Models.WebParameter()
				{
					Type = ERPStore.Models.WebParameterType.Domain,
					Name = "tld",
					Value = parts[0],
				});
			}
			if (parts.Count() > 1)
			{
				result.Add(new ERPStore.Models.WebParameter()
				{
					Type = ERPStore.Models.WebParameterType.Domain,
					Name = "zone",
					Value = parts[1],
				});
			}
			if (parts.Count() > 2)
			{
				parts.RemoveAt(0);
				parts.RemoveAt(0);
				parts.Reverse();
				result.Add(new ERPStore.Models.WebParameter()
				{
					Type = ERPStore.Models.WebParameterType.Domain,
					Name = "host",
					Value = parts.JoinString("."),
				});
			}

			foreach (var item in context.Items)
			{
				var wprm = new ERPStore.Models.WebParameter()
				{
					Name = item.ToString(),
					Value = (string) context.Items[item] ?? string.Empty,
					Type = ERPStore.Models.WebParameterType.Item
				};
				result.Add(wprm);
			}

			return result;
		}

		public static string GetValue(this IEnumerable<ERPStore.Models.WebParameter> webParameters, ERPStore.Models.WebParameterType type, string name)
		{
			var item = webParameters.FirstOrDefault(i => i.Type == type
									&& i.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase));

			if (item != null)
			{
				return item.Value;
			}
			return null;
		}

		public static Exception GetLastERPStoreException(this HttpServerUtility server, System.Web.HttpContext ctx)
		{
			var ex = server.GetLastError();

			if (ex.Message.IndexOf("A potentially dangerous Request") != -1)
			{
				return null;
			}

			var isNewVisitor = false;
			string visitorId = string.Empty;
			try
			{
				visitorId = ctx.GetOrCreateVisitorId(out isNewVisitor);
			}
			catch { }
			ex.Data.Add("Message", ex.Message);
			ex.Data.Add("machineName", Environment.MachineName);
			ex.Data.Add("host", ctx.Request.Url.Host);
			ex.Data.Add("visitorId", visitorId);
			ex.Data.Add("userHostAddress", ctx.Request.UserHostAddress);
			ex.Data.Add("userHostName", ctx.Request.UserHostName);
			ex.Data.Add("url", ctx.Request.RawUrl);
			ex.Data.Add("referer", ctx.Request.UrlReferrer);
			ex.Data.Add("applicationPath", ctx.Request.ApplicationPath);
			ex.Data.Add("user-agent", ctx.Request.Headers["User-Agent"]);
			ex.Data.Add("cookie", ctx.Request.Headers["Cookie"]);
			ex.Data.Add("httpmethod", ctx.Request.HttpMethod.ToString());
			if (ctx.Request.Form.Count > 0)
			{
				ex.Data.Add("begin-form", "-----------------------");
				int i = 1;
				foreach (var item in ctx.Request.Form.AllKeys)
				{
					string key = string.Format("{0}:{1}", item, i);
					ex.Data.Add(key, ctx.Request.Form[item]);
				}
				ex.Data.Add("end-form", "-----------------------");
			}
			if (ctx.User != null
				&& ctx.User.Identity.IsAuthenticated)
			{
				if (ctx.User is ERPStore.Models.UserPrincipal)
				{
					var user = ((ERPStore.Models.UserPrincipal)ctx.User).CurrentUser;
					if (user != null)
					{
						ex.Data.Add("user", user.Login);
					}
					else
					{
						ex.Data.Add("user", "authenticated");
					}
				}
				else
				{
					ex.Data.Add("user", "connected");
				}
			}
			else
			{
				ex.Data.Add("user", "anonymous");
			}

			return ex;
		}

		public static ERPStore.Events.TrackerEvent CreateTrackerEvent(this HttpContextBase ctx, ViewResult viewResult, RouteData routeData)
		{
			var trackerEvent = ctx.CreateTrackerEvent(viewResult);
			trackerEvent.ControllerName = routeData.Values["controller"] as string;
			trackerEvent.ActionName = routeData.Values["actionName"] as string;

			return trackerEvent;
		}


		public static ERPStore.Events.TrackerEvent CreateTrackerEvent(this HttpContextBase ctx, ViewResult viewResult)
		{
			var trackerEvent = new ERPStore.Events.TrackerEvent();

			trackerEvent.UserHostAddress = ctx.Request.UserHostAddress;
			trackerEvent.UserAgent = ctx.Request.UserAgent;
			bool exist = false;
			trackerEvent.VisitorId = ctx.GetOrCreateVisitorId(out exist);
			trackerEvent.RawUrl = ctx.Request.RawUrl;
			trackerEvent.Referer = (ctx.Request.UrlReferrer != null) ? ctx.Request.UrlReferrer.OriginalString : string.Empty;
			trackerEvent.LocalPath = ctx.Request.Url.LocalPath;
			trackerEvent.ResponseStatusCode = ctx.Response.StatusCode;
			if (ctx.Request.UserLanguages != null
				&& ctx.Request.UserLanguages.Length > 0)
			{
				trackerEvent.Language = ctx.Request.UserLanguages[0];
			}
			trackerEvent.OriginalUrl = ctx.Request.Url.OriginalString;
			trackerEvent.ExtendedParameters = ctx.GetWebContextParameterList().ToList();

			if (viewResult != null)
			{
				trackerEvent.ViewName = viewResult.ViewName;
				foreach (var item in viewResult.ViewData.Keys)
				{
					var value = viewResult.ViewData[item];
					if (value != null)
					{
						trackerEvent.ExtendedParameters.Add(new ERPStore.Models.WebParameter()
						{
							Type = ERPStore.Models.WebParameterType.ViewData,
							Name = item,
							Value = value.ToString(),
						});
						// trackerEvent.ExtendedParameters.Add("ViewDataItem:" + item, value.ToString());
					}
				}
			}
			/*
			foreach (var item in ctx.Items.Keys)
			{
				var value = ctx.Items[item];
				if (value != null)
				{
					var key = item.ToString();
					if (!trackerEvent.ExtendedParameters.ContainsKey(key))
					{
						trackerEvent.ExtendedParameters.Add(key, value.ToString());
					}
				}
			}

			if (ctx.Request.Form != null)
			{
				foreach (var item in ctx.Request.Form.AllKeys)
				{
					trackerEvent.ExtendedParameters.Add("FormPost:" + item, ctx.Request.Form[item]);
				}
			}
			*/

			if (ctx.User.Identity.IsAuthenticated)
			{
				if (ctx.User is ERPStore.Models.UserPrincipal)
				{
					trackerEvent.UserId = ((ERPStore.Models.UserPrincipal)ctx.User).CurrentUser.Id;
				}
			}

			return trackerEvent;
		}

	}
}